package core

import (
	"fmt"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/enums"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/tools"
	"reflect"
)

var _ iface.BeanDefinitionRegistryPostProcessor = (*StructPointResolvedPostProcessor)(nil)

type StructPointResolvedPostProcessor struct {
	scannedRefTypeSet map[reflect.Type]struct{}
}

func (my *StructPointResolvedPostProcessor) RepeatableCall() bool {
	return true
}

func (my *StructPointResolvedPostProcessor) PostProcessBeanFactory(iface.ListableBeanFactory) {
}

func (my *StructPointResolvedPostProcessor) PostProcessorBeanDefinitionRegistry(registry iface.BeanDefinitionRegistry) {
	bdNames := registry.GetBeanDefinitionNames()
	if my.scannedRefTypeSet == nil {
		my.scannedRefTypeSet = make(map[reflect.Type]struct{}, len(bdNames)+16)
	}

	for _, beanName := range bdNames {
		err, bd := registry.GetBeanDefinition(beanName)
		if err != nil {
			panic(err)
		}
		my.autoSanneAndRegistry(bd.GetObjRefType(), registry)
	}
}

func (my *StructPointResolvedPostProcessor) autoSanneAndRegistry(refType reflect.Type, registry iface.BeanDefinitionRegistry) {
	refType = tools.ReflectTypeIndirect(refType)
	if refType.Kind() != reflect.Struct {
		return
	}

	if _, ok := my.scannedRefTypeSet[refType]; ok {
		// 扫描过了则跳过
		return
	}

	// 实现了 annotation.ScanStructFieldAnno 接口的结构体才能被扫描
	if !enums.GetBeanKindEnumByType(refType).Is(enums.ScanStructField) {
		return
	}

	my.scannedRefTypeSet[refType] = struct{}{}

	for i := 0; i < refType.NumField(); i++ {
		structField := refType.Field(i)
		rtField := tools.ReflectTypeIndirect(structField.Type)
		if structField.Anonymous {
			// 内嵌字段跳过
			continue
		}
		if annotation.IsAnnotationType(rtField) {
			// 注解类型字段跳过
			continue
		}
		if rtField.Kind() != reflect.Struct {
			// 非结构体跳过
			continue
		}

		// rtField 必须内嵌 annotation.Component 或 annotation.Configuration 或 annotation.ScanStructField
		// 才能被gspring识别并被容器管理
		beanKind := enums.GetBeanKindEnumByType(rtField)
		tools.Assert().IsTrue(beanKind.IsValid(),
			fmt.Sprintf("无效的gspring bean, 缺少配置注解: %s", tools.GetFullPathNameByRt(rtField)))
		beanName := beanKind.ResolveBeanName(rtField)

		bd := NewDefaultBeanDefinition(beanName, rtField)
		if beanFactory, ok := registry.(iface.ListableBeanFactory); ok {
			if !beanFactory.ContainsLocalBean(beanName) {
				registry.RegisterBeanDefinition(beanName, bd)
			}
		} else {
			if !registry.ContainsBeanDefinition(beanName) {
				registry.RegisterBeanDefinition(beanName, bd)
			}
		}
		my.autoSanneAndRegistry(rtField, registry)
	}
}
